{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 基于Tensorflow的softmax回归"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Tensorflow是近年来非常非常流行的一个分布式的机器学习框架，之前一直想学习但是一直被各种各样的事情耽搁着。这学期恰好选了“人工神经网络”这门课，不得不接触这个框架了。最开始依照书上的教程通过Anaconda来配置环境，安装tensorflow。结果tensorflow是安装好了但是用起来是真麻烦。最后卸载了Anaconda在裸机上用`pip install tensorflow`来安装，可是裸机上的python是3.6.3版本的，似乎不支持tensorflow，于是在电脑上安装了另一个版本的python才算解决了这个问题，哎！说多了都是泪。言归正传，现在通过一个softmax实现手写字母识别的例子来正式进入tensorflow学习之旅。\n",
    "\n",
    "softmax是一个非常常见的函数处理方式，它允许我们将模型的输出归一化并且以概率的形式输出，是非常有用的一种处理方式。具体的内容可以参见这个知乎问题[Softmax 函数的特点和作用是什么？](https://www.zhihu.com/question/23765351)\n",
    "\n",
    "## 数据集\n",
    "\n",
    "本例采用的是`mnist`数据集，它在机器学习领域非常有名。首先我们来认识一下这个数据集，tensorflow能够自动下载并使用这个数据集。获取到数据集之后首先查看一下训练集的大小，由于这次softmax回归使用的是mnist中的手写图片作为训练集，因此为了直观地了解一下数据集还需要查看其中的一些手写图片，在这里就用到了matplotlib这个框架来绘图。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Extracting MNIST_data/train-images-idx3-ubyte.gz\n",
      "Extracting MNIST_data/train-labels-idx1-ubyte.gz\n",
      "Extracting MNIST_data/t10k-images-idx3-ubyte.gz\n",
      "Extracting MNIST_data/t10k-labels-idx1-ubyte.gz\n",
      "训练集数据的大小为：(55000, 784)\n"
     ]
    },
    {
     "data": {
      "image/png": "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\n",
      "text/plain": [
       "<Figure size 432x288 with 3 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    }
   ],
   "source": [
    "from tensorflow.examples.tutorials.mnist import input_data\n",
    "import os\n",
    "import matplotlib.pyplot as plt\n",
    "import numpy as np\n",
    "\n",
    "os.environ[\"TF_CPP_MIN_LOG_LEVEL\"]='3'#禁止输出警告信息\n",
    "#加载mnist数据集，one_hot设定为True是使用向量的形式编码数据类别，这主要是考虑到使用softmax\n",
    "mnist = input_data.read_data_sets(\"MNIST_data/\", one_hot=True)\n",
    "print(\"训练集数据的大小为：{0}\".format(mnist.train.images.shape))\n",
    "fig = plt.figure(\"数据展示\")\n",
    "for k in range(3):\n",
    "    result = []\n",
    "    temp = []\n",
    "    img = mnist.train.images[k]#获得第一幅图片，是一个28*28的图片展开成的784维的向量，\n",
    "    for i in range(img.shape[0]):\n",
    "        temp.append(img[i])\n",
    "        if (i + 1) % 28 == 0:\n",
    "            result.append(temp)\n",
    "            temp = []\n",
    "    img = np.matrix(result, dtype=np.float)#获取第一幅图片的矩阵形式\n",
    "    ax = fig.add_subplot(130 + k + 1)\n",
    "    ax.imshow(img)\n",
    "plt.show()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "从上面代码的输出可以看到：该数据集的训练集的规模是55000x784。数据集中每一个行向量代表着一个28x28图片的一维展开，虽然说在图片识别中像素点的位置页蕴含着非常大的信息，但是在这里就不在意那么多了，仅仅将其一维展开就可以。笔者用mat将数据集中的前三个图像画了出来展示在上面，接下来就要用到softmax回归的方法实现一个基本的首写字母识别。\n",
    "\n",
    "## softmax回归\n",
    "\n",
    "在这里简要介绍一下softmax回归的相关知识。softmax回归也是线性回归的一种，只不过是在输出层使用了softmax函数进行处理。具体的训练方法上也是使用了经典的随机梯度下降法训练。其判别函数有如下形式：\n",
    "\n",
    "$$f(x)=wx+b$$\n",
    "\n",
    "**注意：softmax回归可以用于处理多分类问题，因此上式中的所有变量都是矩阵或向量形式。**\n",
    "\n",
    "模型的输出f(x)还并不是最终的输出，还需要用softmax函数进行处理，softmax函数的形式如下所示:\n",
    "\n",
    "$$softmax(x)=\\frac{exp(x_{i})}{\\sum_{j}^{n}exp(x_{j})}$$\n",
    "\n",
    "这样处理后的模型输出可以表达输入数据在各个标签分类上的概率表达，此外使用softmax函数还有着其他很多的好处，最主要的还是在损失函数上的便利。此处不一一列举。\n",
    "\n",
    "softmax回归的损失函数采用信息熵的形式给出：\n",
    "\n",
    "$$H_{y^{'}}(y)=-\\sum y_{i}^{'}ln(y_{i})$$\n",
    "\n",
    "最后，笔者想在softmax函数下推导上述损失函数的随机梯度下降法的迭代公式，虽然tensorflow为我们做了这件事，但是作为算法编写者的我们依然有必要了解这其中的细节。首先，我们需要得到损失函数关于`w`的梯度：\n",
    "\n",
    "$$\\frac{\\partial H_{y^{'}}(y)}{\\partial w}=\\frac{\\partial -\\sum y_{i}^{'}ln(y_{i})}{\\partial w}=\\frac{\\partial -yln(softmax(f(xw + b))}{\\partial w}$$\n",
    "\n",
    "该求导比较复杂，采用链式求导法：\n",
    "\n",
    "$$\\frac{\\partial Loss}{\\partial w}=\\frac{\\partial Loss}{\\partial softmax(xw + b)}\\frac{\\partial softmax(xw + b)}{\\partial xw + b}\\frac{\\partial xw + b}{\\partial w}$$\n",
    "\n",
    "上述链式求导就比较简单，第一项和最后一项的求导都很容易得到，关键是第二项的求导。在这里我们直接给出softmax函数的求导公式。\n",
    "\n",
    "$$\\frac{\\partial softmax(xw + b)}{\\partial xw + b}=softmax(xw + b)(1 - softmax(xw + b))$$\n",
    "\n",
    "又由于上述第一项和第二项的求导为：\n",
    "\n",
    "$$\\frac{\\partial Loss}{\\partial softmax(xw + b)}=\\frac{\\partial -yln(softmax(f(xw + b))}{\\partial softmax(xw + b)}=\\frac{-y}{softmax(xw + b)}$$\n",
    "\n",
    "$$\\frac{\\partial xw + b}{\\partial w}=x$$\n",
    "\n",
    "因此：\n",
    "\n",
    "$$\\frac{\\partial -yln(softmax(f(xw + b))}{\\partial w}=y(softmax(xw + b) - 1)x$$\n",
    "\n",
    "接下来就可以使用随机梯度下降法的迭代公式来迭代求解`w`：\n",
    "\n",
    "$$w=w+\\alpha \\frac{\\partial -yln(softmax(f(xw + b))}{\\partial w}$$\n",
    "\n",
    "## tensroflow实现softmax回归\n",
    "\n",
    "首先我们定义模型中的各个参数，其中x和真实的标签值y_不设定死，使用`placeholder`占据计算图的一个节点。代码如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "import tensorflow as tf\n",
    "\n",
    "session = tf.InteractiveSession()#定义一个交互式会话\n",
    "x = tf.placeholder(tf.float32, [None, 784])\n",
    "w = tf.Variable(tf.zeros([784, 10]))#权重w初始化为0\n",
    "b = tf.Variable(tf.zeros([1, 10]))#bias初始化为0\n",
    "y = tf.nn.softmax(tf.matmul(x, w) + b)\n",
    "y_ = tf.placeholder(tf.float32, [None, 10])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "设定其损失函数`cross_entry`，规定优化目标，初始化全局参数："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "cross_entry = -tf.reduce_mean(tf.reduce_sum(y_ * tf.log(y), reduction_indices=[1]))\n",
    "train_set = tf.train.GradientDescentOptimizer(0.5).minimize(cross_entry)\n",
    "tf.global_variables_initializer().run()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "准备工作都差不多做完了，接下来应该进行模型的训练。在本例中迭代一千次，每次从训练集中随机选取100组数据训练模型。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [],
   "source": [
    "for i in range(1000):\n",
    "    trainSet, trainLabel = mnist.train.next_batch(100)\n",
    "    train_set.run(feed_dict={x: trainSet, y_: trainLabel})"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "以上，我们已经完成了模型的训练，现在应该检测一下模型的效果。我们使用mnist的测试数据来测试模型的分类效果："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "模型的分类正确率为0.917\n"
     ]
    }
   ],
   "source": [
    "accuracy = tf.cast(tf.equal(tf.argmax(y, 1), tf.argmax(y_, 1)), dtype=tf.float32)\n",
    "accuracy = tf.reduce_mean(accuracy)\n",
    "print(\"模型的分类正确率为{0:.3f}\".format(session.run(accuracy, feed_dict={x: mnist.test.images, y_: mnist.test.labels})))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "由上面代码的输出可以看到，该例程的分类准确率还是非常高的，达到了接近92%。这篇入门介绍中用到的softmax回归其实本质上可以看作是一个无隐层的神经网络模型，只拥有一个输入层和一个输出层，二者中间使用了简单的线性方法连接。在实际的手写图片识别中可能并不会用到这样的线性模型，更多的是使用卷积神经网络（CNN）。\n",
    "\n",
    "## 后记\n",
    "\n",
    "这篇文章算是笔者tensorflow入门的一个小应用。说一下我对神经网络，机器学习和tensorflow的看法吧。最近这几年最热的名词可能就是人工智能，深度学习了。笔者也未能免俗，随着这股洪流加入了浩浩荡荡的机器学习大军。从最开始最简单的随机梯度下降法，线性回归到后来有些难的序列最小优化算法，支持向量机。一步步走来发现这个计算机学科的分枝还是非常有意思的，看似非常严谨，枯燥却又十分优美的数学模型竟然能够表现出一丝丝“智能”，有时候真的会惊艳到我。\n",
    "\n",
    "从2006年起，随着计算机计算能力的快速提升，曾经被冷落的神经网络又开始热了起来。对于神经网络，个人对它的未来不是很确定，一则是因为神经网络在历史上的命运可谓是大起大落，曾经的感知机，BP都是炙手可热，却又都草草收场，谁知道这一次的AI火热是不是能持续下去，也许碰到某一个天花板就又归于沉积了呢？说到底目前的AI行业所研究的“弱人工智能”距离真正的AI还是相差甚远。都已经说不清这到底是人类技术的问题，还是哲学的问题。二则是，目前神经网络模型的可解释性太差，相较于SVM这样的模型，人们似乎说不出为什么神经网络能够表现出如此强大的分类能力。三则是，人类在AI行业的探索上总是在刻意模仿自己的大脑，在神经网络的设计中融入了很多人脑中的机制，但这真的是一条正确的路吗？人类飞上蓝天不是靠着挥舞的翅膀而是飞机的机翼。\n",
    "\n",
    "Tensorflow是Google公司推出的一款非常流行的机器学习框架，目前看来已经占据了机器学习框架的绝对霸主地位。对于这些机器学习框架我个人的感觉是不能脱离它们自己闭门造车，但也不能过度依赖。之前笔者是排斥一切框架的，很多经典的机器学习算法都是自己编写。然而到了神经网络这一块，自己再手动编程的代价太大了，于是就不得不入了框架的坑。我的观点是：对于一个算法一定要自己将其弄懂，其中的数学推导搞清楚再看代码，用框架。切忌将模型作为一个黑箱工具来使用，虽然这在短期来看确实效率很高，但长期来看绝对是百害无一利的。学习的过程中还可以自己动手做一些小demo，提升一下编程的乐趣，还是一件非常有益的事情。"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.5.2"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
